﻿using BattleFury.Lib.Maps.Data;
using BattleFury.Lib.Serialization;
using BattleFury.Lib.Tiles.Data;
using BattleFury.Server.Events;
using BattleFury.Server.Wpf.Core.Commands;
using BattleFury.Server.Wpf.Core.ComponentModel;
using BattleFury.Server.Wpf.Core.ViewModels;
using BattleFury.Server.Wpf.Models;
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace BattleFury.Server.Wpf.ViewModels
{
	public class MainViewModel
		: ViewModel
	{
		#region Properties

		public ServerModel ServerSettings { get; set; }
		public FuryServer Server { get; set; }

		#endregion

		#region Dependency Properties

		public static readonly DependencyProperty TabsProperty = DependencyProperty.Register("Tabs", typeof(ObservableCollection<NotificationObject>), typeof(MainViewModel));
		public ObservableCollection<NotificationObject> Tabs
		{
			get { return (ObservableCollection<NotificationObject>)GetValue(TabsProperty); }
			set { SetValue(TabsProperty, value); }
		}

		public static readonly DependencyProperty ActiveTabProperty = DependencyProperty.Register("ActiveTab", typeof(NotificationObject), typeof(MainViewModel));
		public NotificationObject ActiveTab
		{
			get { return (NotificationObject)GetValue(ActiveTabProperty); }
			set { SetValue(ActiveTabProperty, value); }
		}

		public static readonly DependencyProperty StatusProperty = DependencyProperty.Register("Status", typeof(string), typeof(MainViewModel));
		public string Status
		{
			get { return (string)GetValue(StatusProperty); }
			set { SetValue(StatusProperty, value); }
		}

		public static readonly DependencyProperty ServerStatusImageProperty = DependencyProperty.Register("ServerStatusImage", typeof(ImageSource), typeof(MainViewModel));
		public ImageSource ServerStatusImage
		{
			get { return (ImageSource)GetValue(ServerStatusImageProperty); }
			set { SetValue(ServerStatusImageProperty, value); }
		}

		public static readonly DependencyProperty DataStreamProperty = DependencyProperty.Register("DataStream", typeof(ObservableCollection<DataStreamModel>), typeof(MainViewModel));
		public ObservableCollection<DataStreamModel> DataStream
		{
			get { return (ObservableCollection<DataStreamModel>)GetValue(DataStreamProperty); }
			set { SetValue(DataStreamProperty, value); }
		}

		public static readonly DependencyProperty ConnectionsProperty = DependencyProperty.Register("Connections", typeof (ObservableCollection<ConnectionModel>), typeof (MainViewModel));
		public ObservableCollection<ConnectionModel> Connections
		{
			get { return (ObservableCollection<ConnectionModel>) GetValue(ConnectionsProperty); }
			set { SetValue(ConnectionsProperty, value); }
		}

		public static readonly DependencyProperty DataStreamSelectedIndexProperty = DependencyProperty.Register("DataStreamSelectedIndex", typeof(int), typeof(MainViewModel));
		public int DataStreamSelectedIndex
		{
			get { return (int)GetValue(DataStreamSelectedIndexProperty); }
			set { SetValue(DataStreamSelectedIndexProperty, value); }
		}


		#endregion

		#region Commands

		public static readonly DependencyProperty ConnectCommandProperty = DependencyProperty.Register("ConnectCommand", typeof(ICommand), typeof(MainViewModel));
		public ICommand ConnectCommand
		{
			get { return (ICommand)GetValue(ConnectCommandProperty); }
			set { SetValue(ConnectCommandProperty, value); }
		}

		public static readonly DependencyProperty DisconnectCommandProperty = DependencyProperty.Register("DisconnectCommand", typeof(ICommand), typeof(MainViewModel));
		public ICommand DisconnectCommand
		{
			get { return (ICommand)GetValue(DisconnectCommandProperty); }
			set { SetValue(DisconnectCommandProperty, value); }
		}

		public static readonly DependencyProperty SettingsCommandProperty = DependencyProperty.Register("SettingsCommand", typeof(ICommand), typeof(MainViewModel));
		public ICommand SettingsCommand
		{
			get { return (ICommand)GetValue(SettingsCommandProperty); }
			set { SetValue(SettingsCommandProperty, value); }
		}

		public static readonly DependencyProperty ExitCommandProperty = DependencyProperty.Register("ExitCommand", typeof(ICommand), typeof(MainViewModel));
		public ICommand ExitCommand
		{
			get { return (ICommand)GetValue(ExitCommandProperty); }
			set { SetValue(ExitCommandProperty, value); }
		}

		public static readonly DependencyProperty ServerStatusProperty = DependencyProperty.Register("ServerStatus", typeof(ServerStatus), typeof(MainViewModel));
		public ServerStatus ServerStatus
		{
			get { return (ServerStatus)GetValue(ServerStatusProperty); }
			set { SetValue(ServerStatusProperty, value); }
		}
		
		public static readonly DependencyProperty CreateBlankMapCommandProperty = DependencyProperty.Register("CreateBlankMapCommand", typeof (ICommand), typeof (MainViewModel));
		public ICommand CreateBlankMapCommand
		{
			get { return (ICommand) GetValue(CreateBlankMapCommandProperty); }
			set { SetValue(CreateBlankMapCommandProperty, value); }
		}
		
		#endregion

		#region Initialization

		public MainViewModel()
		{
			SetServerStatus(ServerStatus.Disconnected);

			Tabs = new ObservableCollection<NotificationObject>(new List<NotificationObject>());
			DataStream = new ObservableCollection<DataStreamModel>(new List<DataStreamModel>());
			Connections = new ObservableCollection<ConnectionModel>(new List<ConnectionModel>());
			DataStreamSelectedIndex = DataStream.Count - 1;

			RegisterCommands();

			Initialize();
		}

		public void RegisterCommands()
		{
			ConnectCommand = new DelegateCommand(Connect);
			DisconnectCommand = new DelegateCommand(Disconnect);
			SettingsCommand = new DelegateCommand(ShowSettings);
			ExitCommand = new DelegateCommand(Exit);

			CreateBlankMapCommand = new DelegateCommand(CreateBlankMap);
		}

		public void Initialize()
		{
			Tabs.Clear();

			ServerSettings = new ServerModel();
			Tabs.Add(ServerSettings);
			ActiveTab = Tabs[0];

			Server = new FuryServer();
			Server.OnConsoleMessage += ServerConsoleMessageReceived;
			Server.OnClientConnect += ClientConnected;
			Server.OnClientDisconnect += ClientDisconnected;
		}

		#endregion

		#region Actions

		public void Connect(object sender = null)
		{
			SetServerStatus(ServerStatus.Connected);
			ServerSettings.IsConnected = true;

			Server.Port = ServerSettings.Port;
			Server.Start();
		}

		public void Disconnect(object sender = null)
		{
			if (ServerStatus == ServerStatus.Disconnected) return;

			SetServerStatus(ServerStatus.Disconnected);
			ServerSettings.IsConnected = false;

			Server.Shutdown();
		}

		public void ShowSettings(object sender = null)
		{
			Server.SendServerMessage("Hello World!!!!");
		}

		public void Exit(object sender = null)
		{
			Application.Current.MainWindow.Close();
		}

		public void CreateBlankMap(object sender = null)
		{
			const int width = 50;
			const int height = 50;
			const int tileWidth = 32;
			const int tileHeight = 32;

			var map = new TileMapData
			{
				Name = "Generic Map",
				Width = width,
				Height = height,
			};

			var ground = new TileLayerData
			{
				Name = "Ground",
				IsVisible = true,
				TileWidth = tileWidth,
				TileHeight = tileHeight,
			};
			for (var y = 0; y < map.Width; y++)
			{
				for (var x = 0; x < map.Height; x++)
				{
					var tile = new TileData
					{
						X = x,
						Y = y,
						Index = 72,
						Tileset = ".\\gfx\\Tilesets\\Tileset001"
					};
					if (y == 0 || y == 21 || x == 0 || x == 39)
					{
						tile.Index = 101;
					}
					ground.Tiles.Add(tile);
				}
			}
			map.Layers.Add(ground);

			var collision = new CollisionLayerData
			{
				Name = "Collisions",
				IsVisible = true,
				TileWidth = 32,
				TileHeight = 32,
			};
			for (var y = 0; y < map.Width; y++)
			{
				for (var x = 0; x < map.Height; x++)
				{
					if (y != 0 && y != 21 && x != 0 && x != 39) continue;
					var tile = new CollisionTileData
					{
						X = x,
						Y = y,
					};
					
					collision.Tiles.Add(tile);
				}
			}
			map.Layers.Add(collision);

			TileMapSerializer.Serialize(map, "..\\..\\..\\BattleFury\\BattleFury\\bin\\x86\\Debug\\map.xml");
		}

		#endregion

		#region Methods

		public void SetServerStatus(ServerStatus status)
		{
			ServerStatus = status;
			switch (status)
			{
				case ServerStatus.Connected:
					ServerStatusImage = new BitmapImage(new Uri(@"/BattleFury.Server.Wpf;component/Resources/button-green.png", UriKind.RelativeOrAbsolute));
					break;
				case ServerStatus.Disconnected:
					ServerStatusImage = new BitmapImage(new Uri(@"/BattleFury.Server.Wpf;component/Resources/button-red.png", UriKind.RelativeOrAbsolute));
					break;
			}
		}

		#endregion

		#region Events

		private void ServerConsoleMessageReceived(object sender, ConsoleMessageEventArgs e)
		{
			var data = new DataStreamModel
			{
				RawData = e.Message,
			};
			DataStream.Add(data);
			DataStreamSelectedIndex = DataStream.Count - 1;
		}

		private void ClientConnected(object sender, ClientConnectEventArgs e)
		{
			var model = new ConnectionModel(e.ClientId);
			Connections.Add(model);
		}

		private void ClientDisconnected(object sender, ClientDisconnectEventArgs e)
		{
			var connectionsToRemove = Connections.Where(conn => conn.Id == NetUtility.ToHexString(e.ClientId)).ToList();
			foreach (var conn in connectionsToRemove)
			{
				Connections.Remove(conn);
			}
		}

		#endregion
	}
}